package com.hbzhit.common.utils;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hbzhit.common.entity.PageQueryEntity;
import com.hbzhit.common.enums.QueryEnum;
import com.hbzhit.common.exception.RRException;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 分页查询参数
 * @author DING
 * @date 2021/4/7 15:00
 * @version 1.0
 **/
public class Query<T> {
    /**
     * 从map中获取分页信息，组装为分页对象
     * @param params map条件参数
     * @return 分页对象
     */
    public IPage<T> getPage(Map<String, Object> params) {

        //分页对象
        Page<T> page = new Page<>(Constant.INIT_PAGE, Constant.INIT_LIMIT);

        if(params.get(Constant.PAGE) != null){
            page.setCurrent(Long.parseLong((String)params.get(Constant.PAGE)));
        }
        if(params.get(Constant.LIMIT) != null){
            page.setSize(Long.parseLong((String)params.get(Constant.LIMIT)));
        }

        //分页参数
        params.put(Constant.PAGE, page);

        return page;
    }

    /**
     * 从查询表单对象中获取分页信息，组装为分页对象
     * @param queryForm 查询表单对象
     * @return 分页对象
     */
    public IPage<T> getPage(PageQueryEntity queryForm) {
        if (queryForm.getPage() == null) {
            queryForm.setPage(Constant.INIT_PAGE);
        }
        if (queryForm.getLimit() == null) {
            queryForm.setLimit(Constant.INIT_LIMIT);
        }

        //分页对象
        return new Page<>(queryForm.getPage(), queryForm.getLimit());
    }

    /**
     * 自定义是否查询count
     */
    public IPage<T> getPage(PageQueryEntity queryForm, boolean searchCount) {
        if (queryForm.getPage() == null) {
            queryForm.setPage(Constant.INIT_PAGE);
        }
        if (queryForm.getLimit() == null) {
            queryForm.setLimit(Constant.INIT_LIMIT);
        }
        Page<T> page = new Page<>(queryForm.getPage(), queryForm.getLimit());
        page.setSearchCount(searchCount);
        page.setOptimizeCountSql(searchCount);
        return page;
    }

    /**
     * 根据传入参数初始化条件构造器
     * @param params 传入参数
     * @return MyBatis Plus条件构造器
     */
    public QueryWrapper<T> initQueryWrapper(Map<String, Object> params) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();

        /*参数校验*/
        if (params == null) {
            return queryWrapper;
        }
        /*获取参数键集合*/
        Set<String> paramsKeySet = params.keySet();

        /*查询参数键处理*/
        paramsKeySet.forEach(paramKey -> {
            Object paramValue = params.get(paramKey);
            /*参数校验*/
            if (paramKey == null || paramValue == null) {
                return;
            }
            /*字符串去除首尾空格*/
            if (paramValue instanceof String) {
                paramValue = StrUtil.trim((String) paramValue);
                /*"null"字符串兼容处理，替换为""*/
                if (StrUtil.NULL.equals(paramValue)) {
                    paramValue = StrUtil.EMPTY;
                }
            }
            /*查询条件处理*/
            this.queryHandle(queryWrapper, paramKey, paramValue, false);
        });

        /*排序参数处理*/
        Object sortValue = params.get(Constant.SORT);
        if (sortValue != null) {
            String sortValueStr = StrUtil.toString(sortValue);
            this.orderHandle(queryWrapper, sortValueStr);
        }
        // 默认追加创建日期倒序
        queryWrapper.orderByDesc("create_time");
        // 默认追加id排序，防止mysql分页数据顺序错乱
        queryWrapper.orderByAsc("id");

        return queryWrapper;
    }

    /**
     * 查询条件处理
     * @param queryWrapper 当前条件构造器
     * @param paramKey 参数键
     * @param paramValue 参数值
     */
    private void queryHandle(QueryWrapper<T> queryWrapper, String paramKey, Object paramValue, boolean strictMode) {
        if (paramKey == null || StrUtil.isBlank(paramKey) || paramValue == null) {
            return;
        }
        List<String> paramKeys = StrUtil.splitTrim(paramKey, ",");
        String paramValueStr = StrUtil.toString(paramValue);
        List<String> paramValues = StrUtil.splitTrim(paramValueStr, ",");
        if (paramKeys.size() == 1) {
            paramKey = paramKeys.get(0);
        }
        /*多字段处理*/
        if (paramKeys.size() > 1) {
            // 判断是否需要处理，如果包含空判断，值为空也要处理
            long count = paramKeys.stream().filter(key -> StrUtil.containsAny(key, QueryEnum.NL.getValue(), QueryEnum.NNL.getValue(), QueryEnum.EP.getValue(), QueryEnum.NEP.getValue())).count();
            queryWrapper.and(StrUtil.isNotBlank(paramValueStr) || count > 0, wrapper -> paramKeys.forEach(key -> {
                this.queryHandle(wrapper, key, paramValue, true);
                wrapper.or();
            }));
        }
        /*拼装等于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.EQ.getValue())) {
            queryWrapper.eq(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.EQ.getValue()), paramValue);
        }
        /*拼装不等于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.NE.getValue())) {
            queryWrapper.ne(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.NE.getValue()), paramValue);
        }
        /*拼装大于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.GT.getValue())) {
            queryWrapper.gt(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.GT.getValue()), paramValue);
        }
        /*拼装大于等于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.GE.getValue())) {
            queryWrapper.ge(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.GE.getValue()), paramValue);
        }
        /*拼装小于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.LT.getValue())) {
            queryWrapper.lt(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.LT.getValue()), paramValue);
        }
        /*拼装小于等于参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.LE.getValue())) {
            queryWrapper.le(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.LE.getValue()), paramValue);
        }
        /*拼装全模糊匹配参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.LK.getValue())) {
            paramValueStr = paramValueStr.replaceAll(" ", "%");
            queryWrapper.like(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.LK.getValue()), paramValueStr);
        }
        /*拼装左模糊匹配参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.LKL.getValue())) {
            queryWrapper.likeLeft(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.LKL.getValue()), paramValue);
        }
        /*拼装右模糊匹配参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.LKR.getValue())) {
            queryWrapper.likeRight(StrUtil.isNotBlank(paramValueStr), this.paramKeyHandle(paramKey, QueryEnum.LKR.getValue()), paramValue);
        }
        /*拼装空值NULL参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.NL.getValue())) {
            queryWrapper.isNull(this.paramKeyHandle(paramKey, QueryEnum.NL.getValue()));
        }
        /*拼装不为空值NOT NULL参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.NNL.getValue())) {
            queryWrapper.isNotNull(this.paramKeyHandle(paramKey, QueryEnum.NNL.getValue()));
        }
        /*拼装空字符参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.EP.getValue())) {
            queryWrapper.eq(this.paramKeyHandle(paramKey, QueryEnum.EP.getValue()), "");
        }
        /*拼装不为空字符参数*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.NEP.getValue())) {
            queryWrapper.ne(this.paramKeyHandle(paramKey, QueryEnum.NEP.getValue()), "");
        }
        /*拼装包含参数，多值*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.IN.getValue())) {
            queryWrapper.in(paramValues.size() > 0, this.paramKeyHandle(paramKey, QueryEnum.IN.getValue()), paramValues);
        }
        /*拼装不包含参数，多值*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.NIN.getValue())) {
            queryWrapper.notIn(paramValues.size() > 0, this.paramKeyHandle(paramKey, QueryEnum.NIN.getValue()), paramValues);
        }
        /*拼装介于参数，多值*/
        else if (StrUtil.isWrap(paramKey, "", QueryEnum.BT.getValue())) {
            // 介于参数值个数
            int btValLen = 2;
            if (paramValues.size() != btValLen) {
                throw new RRException("介于参数值异常", "介于参数值中强制有且仅有一个逗号【,】分隔符。");
            }
            queryWrapper.between(this.paramKeyHandle(paramKey, QueryEnum.BT.getValue()), paramValues.get(0), paramValues.get(1));
        } else if (strictMode) {
            throw new RRException("查询参数异常", "多字段等严格模式下强制每个字段必须带查询后缀。");
        }
        // 排除关键字段，其它字段作为eq查询。 //暂不启用，报错率高，不推荐使用！
        /*else {
            if (!StrUtil.equals(paramKey, Constant.SORT)
                    && !StrUtil.equals(paramKey, Constant.PAGE)
                    && !StrUtil.equals(paramKey, Constant.LIMIT)
                    && !StrUtil.equals(paramKey, Constant.TIMESTAMP)
            ) {
                queryWrapper.eq(StrUtil.isNotBlank(paramValueStr), paramKey, paramValue);
            }
        }*/
    }

    /**
     * 排序处理
     * @param queryWrapper 当前条件构造器
     * @param sortValue 排序参数值
     */
    private void orderHandle(QueryWrapper<T> queryWrapper, String sortValue) {
        // 排序处理，sort字段，格式 sort: 'id,code.ASC,name.DESC'
        if (StrUtil.isBlank(sortValue)) {
            return;
        }
        List<String> sortValues = StrUtil.splitTrim(sortValue, ",");
        sortValues.forEach(value -> {
            if (StrUtil.isWrap(value, "", Constant.SORT_DESC)) {
                queryWrapper.orderByDesc(this.paramKeyHandle(value, Constant.SORT_DESC));
            }
            else if (StrUtil.isWrap(value, "", Constant.SORT_ASC)) {
                queryWrapper.orderByAsc(this.paramKeyHandle(value, Constant.SORT_ASC));
            }
            else if (!StrUtil.contains(value, ".")) {
                queryWrapper.orderByAsc(this.paramKeyHandle(value, ""));
            } else {
                throw new RRException("排序参数异常", StrUtil.format("排序参数只能为{}（可省略）或{}。", Constant.SORT_ASC, Constant.SORT_DESC));
            }
        });
    }

    /**
     * 前端查询字段转数据库字段处理
     * @param paramKey 前端查询字段
     * @param suffix 条件后缀
     */
    private String paramKeyHandle(String paramKey, String suffix) {
        return StrUtil.toUnderlineCase(StrUtil.unWrap(paramKey, "", suffix));
    }
}
